use std::{error::Error, ptr, fmt::Display};

extern {    
    /**
     Constructs the suffix array of a given string.
     * T \[0..n-1\] The input string.
     * SA \[0..n-1+fs\] The output array of suffixes.
     * n The length of the given string.
     * fs The extra space available at the end of SA array (0 should be enough for most cases).
     * freq \[0..255\] The output symbol frequency table (can be NULL).
     * return 0 if no error occurred, -1 or -2 otherwise.
     */
    fn libsais(
        T: *const u8, 
        SA: *mut i32, 
        n: i32, 
        fs: i32, 
        freq: *mut i32
    ) -> i32;
}

#[derive(Debug)]
pub struct OperationError(pub i32);

impl Display for OperationError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("OperationError: {}", self.0))
    }
}

impl Error for OperationError { }

pub fn libsais_u8_i32(
    t: &[u8], 
    fs: i32, 
    with_freq: bool 
) -> Result<(Option<Vec<i32>>, Vec<i32>), OperationError> {
    unsafe {
        if fs < 0 {
            panic!("[fs] should not be negative!");
        }
        let n = t.len() as i32;
        let mut out_freq_opt = if with_freq { Some(Vec::with_capacity(256)) } else { None };
        let mut out_sa: Vec<i32> = Vec::with_capacity((n + fs) as usize);

        let r = 
            libsais(
                t.as_ptr(), 
                out_sa.as_mut_ptr(), 
                n, 
                fs, 
                match out_freq_opt { 
                    Some(ref mut b) => b.as_mut_ptr(),
                    None => ptr::null_mut() 
                }
            ); 
        
        if r < 0 {
            Err(OperationError(r))
        } else {
            out_sa.set_len(out_sa.capacity());
            if let Some(ref mut freq) = out_freq_opt {
                freq.set_len(freq.capacity());
            }
            Ok((out_freq_opt, out_sa))
        }      
    }
}